#ifndef UUID_19FC39B57A0B468C7CCB718F7935BFB2
#define UUID_19FC39B57A0B468C7CCB718F7935BFB2

//#include "../luautil/luautil.hpp"
#include <Box2D/Dynamics/b2World.h>
#include <Box2D/Dynamics/b2Fixture.h>
#include <gvl/hash_set/hash_set.hpp>

template<typename T>
struct NumberedIndex
{
	NumberedIndex()
	: first(0)
	{
	}

	NumberedIndex(unsigned firstInit, T secondInit)
	: first(firstInit)
	, second(secondInit)
	{
	}

	operator bool() const
	{ return first != 0; }

	gvl::hash_t hash(unsigned p) const
	{ return gvl::hash(first, p); }

	bool operator==(NumberedIndex const& other) const
	{ return first == other.first; }

	bool operator==(unsigned k) const
	{ return first == k; }

	unsigned first;
	T second;
};

struct LuaWorld;

struct LuaBody
{
	LuaBody(b2Body* body, LuaWorld* owner)
	: body(body)
	, owner(owner)
	{
	}

	b2Body* body;
	LuaWorld* owner;

	void invalidate()
	{
		body = 0;
	}
};

struct LuaWorld : b2World, b2DestructionListener, b2ContactListener
{
	LuaWorld()
	: b2World(b2Vec2(0.f, 20.f), true)
	, nextBodyId(1)
	, nextShapeId(1)
	, nextJointId(1)
	{
		SetDestructionListener(this);
	}

	LuaWorld(LuaWorld const& other)
	: b2World(other)
	, nextBodyId(other.nextBodyId)
	, nextShapeId(other.nextShapeId)
	, nextJointId(other.nextJointId)
	{
		SetDestructionListener(this);

		/* TODO:
		   If we're going to have userdatas representing bodies, we're going to have to
		   produce mapping tables from old to new when copying. Since lua's interface
		   is annoying, it's complicated to do this on the C++ side as we would need to
		   store the userdatas in the registry or something. It's probably easiest to
		   do on the lua side by explicitly creating the body userdatas after copying and
		   filling in the tables.
		*/

		for(b2Body* body = GetBodyList(); body; body = body->GetNext())
		{
			aliveBodies.insert(NumberedIndex<void*>(unsigned(body->GetUserData()), body));
		}
	}

	~LuaWorld()
	{
	}

	/*override*/ void SayGoodbye(b2Joint* joint)
	{
		
	}

	/*override*/ void SayGoodbye(b2Fixture* fixture)
	{
		unsigned id = (unsigned)fixture->GetUserData();
		aliveShapes.erase(id);
	}

	/*override*/ void BeginContact(b2Contact* contact)
	{
		newContacts.push_back(contact);
	}

	/*override*/ void EndContact(b2Contact* /*contact*/)
	{
	}

	b2Body* check_body(lua_State* L, int idx)
	{
		unsigned id = luaL_checkint(L, idx);
		auto pair = aliveBodies.get(id);
		if(!pair)
			luaL_argerror(L, idx, "expected valid body");
		return static_cast<b2Body*>(pair->second);
	}

	b2Body* get_body(unsigned id)
	{
		auto pair = aliveBodies.get(id);
		if(!pair)
			return 0;
		return static_cast<b2Body*>(pair->second);
	}

	void addUserdata(LuaBody* ud)
	{
		bodyUserdata.push_back(ud);
	}

	void removeUserdata(LuaBody* ud)
	{
		for(std::size_t i = 0; i < bodyUserdata.size(); ++i)
		{
			if(bodyUserdata[i] == ud)
			{
				// Replace with last
				bodyUserdata[i] = bodyUserdata.back();
				bodyUserdata.pop_back();
				break;
			}
		}
	}

	unsigned nextBodyId;
	unsigned nextShapeId;
	unsigned nextJointId;
	gvl::hash_set_unboxed<NumberedIndex<void*> > aliveBodies;
	gvl::hash_set_unboxed<NumberedIndex<void*> > aliveShapes;

	std::vector<b2Contact*> newContacts;

	// A list of alive body userdatas
	std::vector<LuaBody*> bodyUserdata;
};


#endif // UUID_19FC39B57A0B468C7CCB718F7935BFB2
